/* 
* Copyright (C) 2021 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.reactivex.rxjava3.openharmony.plugins;

import io.reactivex.rxjava3.core.Scheduler;
import io.reactivex.rxjava3.exceptions.Exceptions;
import io.reactivex.rxjava3.functions.Function;

import java.util.concurrent.Callable;

/**
 * Utility class to inject handlers to certain standard Rxohos operations.
 */
public final class RxOpenHarmonyPlugins {
    private static volatile Function<Callable<Scheduler>, Scheduler> onInitMainThreadHandler;
    private static volatile Function<Scheduler, Scheduler> onMainThreadHandler;

    /**
     * Method to set a Handler function for the initialization of main thread
     *
     * @param handler assigned callable handler
     */
    public static void setInitMainThreadSchedulerHandler(Function<Callable<Scheduler>, Scheduler> handler) {
        onInitMainThreadHandler = handler;
    }

    /**
     * Method for the initialization of main thread scheduler
     *
     * @param scheduler callable instance for the given scheduler
     * @return scheduler for main thread
     */
    public static Scheduler initMainThreadScheduler(Callable<Scheduler> scheduler) {
        if (scheduler == null) {
            throw new NullPointerException("scheduler == null");
        }
        Function<Callable<Scheduler>, Scheduler> func = onInitMainThreadHandler;
        if (func == null) {
            return callRequireNonNull(scheduler);
        }
        return applyRequireNonNull(func, scheduler);
    }

    /**
     * Method to set the main thread scheduler handler
     *
     * @param handler assigned handler
     */
    public static void setMainThreadSchedulerHandler(Function<Scheduler, Scheduler> handler) {
        onMainThreadHandler = handler;
    }

    /**
     * Method will be invoked when scheduler executes actions on the main thread
     *
     * @param scheduler which executes actions on the openharmony main thread
     * @return scheduler will post asynchronous messages to the looper
     */
    public static Scheduler onMainThreadScheduler(Scheduler scheduler) {
        if (scheduler == null) {
            throw new NullPointerException("scheduler == null");
        }
        Function<Scheduler, Scheduler> func = onMainThreadHandler;
        if (func == null) {
            return scheduler;
        }
        return apply(func, scheduler);
    }

    /**
     * Returns the current hook function.
     *
     * @return the hook function, may be null
     */
    public static Function<Callable<Scheduler>, Scheduler> getInitMainThreadSchedulerHandler() {
        return onInitMainThreadHandler;
    }

    /**
     * Returns the current hook function.
     *
     * @return the hook function, may be null
     */
    public static Function<Scheduler, Scheduler> getOnMainThreadSchedulerHandler() {
        return onMainThreadHandler;
    }

    /**
     * Removes all handlers and resets the default behavior.
     */
    public static void reset() {
        setInitMainThreadSchedulerHandler(null);
        setMainThreadSchedulerHandler(null);
    }

    static Scheduler callRequireNonNull(Callable<Scheduler> schedulerCallable) {
        try {
            Scheduler scheduler = schedulerCallable.call();
            if (scheduler == null) {
                throw new NullPointerException("Scheduler Callable returned null");
            }
            return scheduler;
        } catch (Throwable ex) {
            throw Exceptions.propagate(ex);
        }
    }

    static Scheduler applyRequireNonNull(Function<Callable<Scheduler>,
            Scheduler> func, Callable<Scheduler> schedulerCallable) {
        Scheduler scheduler = apply(func, schedulerCallable);
        if (scheduler == null) {
            throw new NullPointerException("Scheduler Callable returned null");
        }
        return scheduler;
    }

    static <T, R> R apply(Function<T, R> func, T type) {
        try {
            return func.apply(type);
        } catch (Throwable ex) {
            throw Exceptions.propagate(ex);
        }
    }

    private RxOpenHarmonyPlugins() {
        throw new AssertionError("No instances.");
    }
}
